数据科学实战:K-Means 广告效果聚类分析
The following article is from Python数据科学修炼之路 Author 老杨啊小强
关注上方“Python数据科学”,选择星标,
关键时间,第一时间送达!
二、数据介绍
三、导入库,加载数据
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler,OneHotEncoder
from sklearn.metrics import silhouette_score # 导入轮廓系数指标
from sklearn.cluster import KMeans # KMeans模块
%matplotlib inline
## 设置属性防止中文乱码
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
raw_data.head()
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间 素材类型 广告类型 合作方式 广告尺寸 广告卖点
0 A203 3.69 0.0071 0.0214 2.3071 419.77 0.0258 20 jpg banner roi 140*40 打折
1 A387 178.70 0.0040 0.0324 2.0489 157.94 0.0030 19 jpg banner cpc 140*40 满减
2 A388 91.77 0.0022 0.0530 1.8771 357.93 0.0026 4 jpg banner cpc 140*40 满减
3 A389 1.09 0.0074 0.3382 4.2426 364.07 0.0153 10 jpg banner cpc 140*40 满减
4 A390 3.37 0.0028 0.1740 2.1934 313.34 0.0007 30 jpg banner cpc 140*40 满减
四、数据审查
print('{:*^60}'.format('数据前两行:'))
print(raw_data.head(2)) # 打印输出前2条数据
print('{:*^60}'.format('数据类型:'))
print(pd.DataFrame(raw_data.dtypes).T) # 打印数据类型分布
print('{:*^60}'.format('数据统计描述:'))
print(raw_data.describe().round(2).T) # 打印原始数据基本描述性信息
***************************数据前两行:***************************
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间 素材类型 广告类型 \
0 A203 3.69 0.0071 0.0214 2.3071 419.77 0.0258 20 jpg banner
1 A387 178.70 0.0040 0.0324 2.0489 157.94 0.0030 19 jpg banner
合作方式 广告尺寸 广告卖点
0 roi 140*40 打折
1 cpc 140*40 满减
***************************数据类型:****************************
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间 \
0 object float64 float64 float64 float64 float64 float64 int64
素材类型 广告类型 合作方式 广告尺寸 广告卖点
0 object object object object object
**************************数据统计描述:***************************
count mean std min 25% 50% 75% max
日均UV 889.0 540.85 1634.41 0.06 6.18 114.18 466.87 25294.77
平均注册率 889.0 0.00 0.00 0.00 0.00 0.00 0.00 0.04
平均搜索量 889.0 0.03 0.11 0.00 0.00 0.00 0.01 1.04
访问深度 889.0 2.17 3.80 1.00 1.39 1.79 2.22 98.98
平均停留时间 887.0 262.67 224.36 1.64 126.02 236.55 357.98 4450.83
订单转化率 889.0 0.00 0.01 0.00 0.00 0.00 0.00 0.22
投放总时间 889.0 16.05 8.51 1.00 9.00 16.00 24.00 30.00
na_cols = raw_data.isnull().any(axis=0) # 查看每一列是否具有缺失值
print('{:*^60}'.format('含有缺失值的列:'))
print(na_cols[na_cols==True]) # 查看具有缺失值的列
print('总共有多少数据缺失: {0}'.format(raw_data.isnull().any(axis=1).sum())) # 查看具有缺失值的行总记录数
**************************含有缺失值的列:**************************
平均停留时间 True
dtype: bool
总共有多少数据缺失: 2
# 也可以用下面的代码查看
raw_data.isnull().sum().sort_values(ascending=False)
print('{:*^60}'.format('Correlation analysis:'))
print(raw_data.corr().round(2).T) # 打印原始数据相关性信息
*******************Correlation analysis:********************
日均UV 平均注册率 平均搜索量 访问深度 平均停留时间 订单转化率 投放总时间
日均UV 1.00 -0.05 -0.07 -0.02 0.04 -0.05 -0.04
平均注册率 -0.05 1.00 0.24 0.11 0.22 0.32 -0.01
平均搜索量 -0.07 0.24 1.00 0.06 0.17 0.13 -0.03
访问深度 -0.02 0.11 0.06 1.00 0.72 0.16 0.06
平均停留时间 0.04 0.22 0.17 0.72 1.00 0.25 0.05
订单转化率 -0.05 0.32 0.13 0.16 0.25 1.00 -0.00
投放总时间 -0.04 -0.01 -0.03 0.06 0.05 -0.00 1.00
# 相关性可视化展示
import seaborn as sns
corr=raw_data.corr().round(2)
sns.heatmap(corr,cmap="Reds",annot=True)
五、数据处理
raw_data2 = raw_data.drop(['平均停留时间'], axis=1)
cols=["素材类型","广告类型","合作方式","广告尺寸","广告卖点"]
for x in cols:
data=raw_data2[x].unique()
print("变量【{0}】的取值有:\n{1}".format(x,data))
print("-·"*20)
变量【素材类型】的取值有:
['jpg' 'swf' 'gif' 'sp']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告类型】的取值有:
['banner' 'tips' '不确定' '横幅' '暂停']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【合作方式】的取值有:
['roi' 'cpc' 'cpm' 'cpd']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告尺寸】的取值有:
['140*40' '308*388' '450*300' '600*90' '480*360' '960*126' '900*120'
'390*270']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
变量【广告卖点】的取值有:
['打折' '满减' '满赠' '秒杀' '直降' '满返']
-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·-·
cols = ['素材类型','广告类型','合作方式','广告尺寸','广告卖点']
model_ohe = OneHotEncoder(sparse=False) # 建立OneHotEncode对象
ohe_matrix = model_ohe.fit_transform(raw_data2[cols]) # 直接转换
print(ohe_matrix[:2])
out:
[[0. 1. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 1. 1. 0. 0. 0. 0. 0. 0. 0. 1. 0. 0.
0. 0. 0.]
[0. 1. 0. 0. 1. 0. 0. 0. 0. 1. 0. 0. 0. 1. 0. 0. 0. 0. 0. 0. 0. 0. 1. 0.
0. 0. 0.]]
# 用pandas的方法
ohe_matrix1=pd.get_dummies(raw_data2[cols])
ohe_matrix1.head(5)
sacle_matrix = raw_data2.iloc[:, 1:7] # 获得要转换的矩阵
model_scaler = MinMaxScaler() # 建立MinMaxScaler模型对象
data_scaled = model_scaler.fit_transform(sacle_matrix) # MinMaxScaler标准化处理
print(data_scaled.round(2))
[[0. 0.18 0.02 0.01 0.12 0.66]
[0.01 0.1 0.03 0.01 0.01 0.62]
[0. 0.06 0.05 0.01 0.01 0.1 ]
...
[0.01 0.01 0. 0. 0. 0.72]
[0.05 0. 0. 0. 0. 0.31]
[0. 0. 0. 0.53 0. 0.62]]
X = np.hstack((data_scaled, ohe_matrix))
六、建立模型
score_list = list() # 用来存储每个K下模型的平局轮廓系数
silhouette_int = -1 # 初始化的平均轮廓系数阀值
for n_clusters in range(2, 8): # 遍历从2到5几个有限组
model_kmeans = KMeans(n_clusters=n_clusters) # 建立聚类模型对象
labels_tmp = model_kmeans.fit_predict(X) # 训练聚类模型
silhouette_tmp = silhouette_score(X, labels_tmp) # 得到每个K下的平均轮廓系数
if silhouette_tmp > silhouette_int: # 如果平均轮廓系数更高
best_k = n_clusters # 保存K将最好的K存储下来
silhouette_int = silhouette_tmp # 保存平均轮廓得分
best_kmeans = model_kmeans # 保存模型实例对象
cluster_labels_k = labels_tmp # 保存聚类标签
score_list.append([n_clusters, silhouette_tmp]) # 将每次K及其得分追加到列表
print('{:*^60}'.format('K值对应的轮廓系数:'))
print(np.array(score_list)) # 打印输出所有K下的详细得分
print('最优的K值是:{0} \n对应的轮廓系数是:{1}'.format(best_k, silhouette_int))
*************************K值对应的轮廓系数:*************************
[[2. 0.38655493]
[3. 0.45864451]
[4. 0.50209812]
[5. 0.4800359 ]
[6. 0.47761127]
[7. 0.4935842 ]]
最优的K值是:4
对应的轮廓系数是: 0.5020981194788053
轮廓系数确定最佳的K值,确定数据分为几类较为合理,开篇技术介绍有轮廓系数的介绍了,聚类效果的评价,之前文章有介绍过聚类数和簇内均方误差绘图出现拐点的时候可以确定为几类。
总体思想(评价指标)还是怎么聚才能使得簇内距离足够小,簇与簇之间平均距离足够大来评判。
当然,有兴趣的同学,可以查资料总结一下聚类的评价指标,怎么确定K值,K-Means有什么优缺点,怎么提升?··· ···
后续文章也可能会专门出个这方面的专题。
cluster_labels = pd.DataFrame(cluster_labels_k, columns=['clusters']) # 获得训练集下的标签信息
merge_data = pd.concat((raw_data2, cluster_labels), axis=1) # 将原始处理过的数据跟聚类标签整合
merge_data.head()
渠道代号 日均UV 平均注册率 平均搜索量 访问深度 订单转化率 投放总时间 素材类型 广告类型 合作方式 广告尺寸 广告卖点 clusters
0 A203 3.69 0.0071 0.0214 2.3071 0.0258 20 jpg banner roi 140*40 打折 0
1 A387 178.70 0.0040 0.0324 2.0489 0.0030 19 jpg banner cpc 140*40 满减 0
2 A388 91.77 0.0022 0.0530 1.8771 0.0026 4 jpg banner cpc 140*40 满减 0
3 A389 1.09 0.0074 0.3382 4.2426 0.0153 10 jpg banner cpc 140*40 满减 0
4 A390 3.37 0.0028 0.1740 2.1934 0.0007 30 jpg banner cpc 140*40 满减 0
然后看看,每个类别下的样本数量和占比情况。
# 计算每个聚类类别下的样本量和样本占比
clustering_count = pd.DataFrame(merge_data['渠道代号'].groupby(merge_data['clusters']).count()).T.rename({'渠道代号': 'counts'}) # 计算每个聚类类别的样本量
clustering_ratio = (clustering_count / len(merge_data)).round(2).rename({'counts': 'percentage'}) # 计算每个聚类类别的样本量占比
print(clustering_count)
print("#"*30)
print(clustering_ratio)
clusters 0 1 2 3
counts 154 313 349 73
##############################
clusters 0 1 2 3
percentage 0.17 0.35 0.39 0.08
接下来,我们看看每个类别内部最显著的特征:
# 计算各个聚类类别内部最显著特征值
cluster_features = [] # 空列表,用于存储最终合并后的所有特征信息
for line in range(best_k): # 读取每个类索引
label_data = merge_data[merge_data['clusters'] == line] # 获得特定类的数据
part1_data = label_data.iloc[:, 1:7] # 获得数值型数据特征
part1_desc = part1_data.describe().round(3) # 得到数值型特征的描述性统计信息
merge_data1 = part1_desc.iloc[2, :] # 得到数值型特征的均值
part2_data = label_data.iloc[:, 7:-1] # 获得字符串型数据特征
part2_desc = part2_data.describe(include='all') # 获得字符串型数据特征的描述性统计信息
merge_data2 = part2_desc.iloc[2, :] # 获得字符串型数据特征的最频繁值
merge_line = pd.concat((merge_data1, merge_data2), axis=0) # 将数值型和字符串型典型特征沿行合并
cluster_features.append(merge_line) # 将每个类别下的数据特征追加到列表
# 输出完整的类别特征信息
cluster_pd = pd.DataFrame(cluster_features).T # 将列表转化为矩阵
print('{:*^60}'.format('每个类别主要的特征:'))
all_cluster_set = pd.concat((clustering_count, clustering_ratio, cluster_pd),axis=0) # 将每个聚类类别的所有信息合并
all_cluster_set
*******************每个类别主要的特征:*****************
0 1 2 3
counts 154 313 349 73
percentage 0.17 0.35 0.39 0.08
日均UV 2717.42 1390.01 933.015 1904.37
平均注册率 0.005 0.003 0.003 0.003
平均搜索量 0.051 0.152 0.064 0.106
访问深度 0.947 1.168 5.916 0.943
订单转化率 0.007 0.017 0.006 0.009
投放总时间 8.529 8.199 8.77 8.217
素材类型 jpg swf jpg swf
广告类型 banner 不确定 横幅 tips
合作方式 cpc roi cpc cpm
广告尺寸 308*388 600*90 600*90 450*300
广告卖点 满减 打折 直降 打折
上面看着挺累的,不直观,我们图形化输出:
#各类别数据预处理
num_sets = cluster_pd.iloc[:6, :].T.astype(np.float64) # 获取要展示的数据
num_sets_max_min = model_scaler.fit_transform(num_sets) # 获得标准化后的数据
# 画图
fig = plt.figure(figsize=(6,6)) # 建立画布
ax = fig.add_subplot(111, polar=True) # 增加子网格,注意polar参数
labels = np.array(merge_data1.index) # 设置要展示的数据标签
cor_list = ['g', 'r', 'y', 'b'] # 定义不同类别的颜色
angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False) # 计算各个区间的角度
angles = np.concatenate((angles, [angles[0]])) # 建立相同首尾字段以便于闭合
# 画雷达图
for i in range(len(num_sets)): # 循环每个类别
data_tmp = num_sets_max_min[i, :] # 获得对应类数据
data = np.concatenate((data_tmp, [data_tmp[0]])) # 建立相同首尾字段以便于闭合
ax.plot(angles, data, 'o-', c=cor_list[i], label="第%d类渠道"%(i)) # 画线
ax.fill(angles, data,alpha=2.5)
# 设置图像显示格式
ax.set_thetagrids(angles * 180 / np.pi, labels, fontproperties="SimHei") # 设置极坐标轴
ax.set_title("各聚类类别显著特征对比", fontproperties="SimHei") # 设置标题放置
ax.set_rlim(-0.2, 1.2) # 设置坐标轴尺度范围
plt.legend(loc="upper right" ,bbox_to_anchor=(1.2,1.0)) # 设置图例位置
用了这个jupyter插件,我已经半个月没打开过excel了
特别推荐